package bvrtc

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"sync"
	"time"

	"github.com/pion/ice/v4"
	"github.com/pion/rtcp"
	"github.com/pion/rtp"
	"github.com/pion/transport/v3/packetio"
	"gogs.smarteye.com/smarteye/gortc/pkg/description"
	"gogs.smarteye.com/smarteye/gortc/pkg/format"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtptime"
)

// 定制版

type Packet struct {
	Media  *Media
	Format *Format
	RTP    *rtp.Packet
	PTS    time.Duration
	NTP    time.Time
}

type Handle interface {
	OnPacketRTP(packet *Packet)
	OnPacketRTCP(packet *rtcp.Header, rawPacket []byte)

	OnPacketLost(error)
	OnDecodeError(error)
	OnSessionError(error)
}

type PeerConnection struct {
	handle Handle

	localMedias  map[*description.Media]*Media // video/audio/application
	remoteMedias map[*description.Media]*Media // video/audio/application

	timeDecoder *rtptime.GlobalDecoder // rtp时间戳解码器

	conn    net.Conn // 网络连接
	udpConn *net.UDPConn
	tcpConn *net.TCPConn
}

func NewPeerConnection(h Handle) *PeerConnection {
	if h == nil {
		panic("handle is nil")
	}

	return &PeerConnection{
		handle:       h,
		localMedias:  map[*description.Media]*Media{},
		remoteMedias: map[*description.Media]*Media{},
		timeDecoder:  rtptime.NewGlobalDecoder(),
	}
}

// Close 关闭
func (c *PeerConnection) Close() {
	for _, media := range c.remoteMedias {
		media.close()
	}
	for _, media := range c.localMedias {
		media.close()
	}

	if conn := c.conn; conn != nil {
		_ = conn.Close()
	}
}

// 主动: CreateOffer -> SetLocalDescription -> SetRemoteDescription -> Start
// 被动: SetRemoteDescription -> CreateAnswer -> SetLocalDescription -> Start
// -----------------------------------------------------------------------------------------------------

func (c *PeerConnection) CreateOffer(offer *description.Session) (*description.Session, error) {
	return offer, nil
}

func (c *PeerConnection) CreateAnswer(answer *description.Session) (*description.Session, error) {
	return answer, nil
}

func (c *PeerConnection) SetLocalDescription(desc *description.Session) {
	for _, m := range desc.Medias {
		c.localMedias[m] = newMedia(c, m, true)
	}
}

func (c *PeerConnection) SetRemoteDescription(desc *description.Session) {
	for _, m := range desc.Medias {
		c.remoteMedias[m] = newMedia(c, m, false)
	}
}

func (c *PeerConnection) Start() error {
	for m, _ := range c.remoteMedias {
		if m.Candidate == nil {
			continue
		}

		if m.Candidate.Type() != ice.CandidateTypeHost {
			continue
		}

		switch m.Candidate.NetworkType() {
		case ice.NetworkTypeUDP4:
			// 优先使用内网地址
			if conn, err := net.DialUDP("udp4", nil, &net.UDPAddr{
				IP:   net.ParseIP(m.Candidate.Address()),
				Port: m.Candidate.Port(),
			}); err == nil {
				c.udpConn = conn
				c.conn = c.udpConn

				log.Printf("connect UDP %v success", m.Candidate.Address())
				go c.readUDP()
			} else {
				log.Printf("udp dial failed. %v %v", m.Candidate.String(), err)
			}
		case ice.NetworkTypeTCP4:
			if conn, err := net.DialTCP("tcp4", nil, &net.TCPAddr{
				IP:   net.ParseIP(m.Candidate.Address()),
				Port: m.Candidate.Port(),
			}); err == nil {
				c.tcpConn = conn
				c.conn = c.tcpConn

				log.Printf("connect TCP %v success", m.Candidate.Address())
				go c.readTCP()
			} else {
				log.Printf("tcp dial failed. %v %v", m.Candidate.String(), err)
			}
		}
	}

	if c.conn == nil {
		return fmt.Errorf("no conn")
	}

	return nil
}

// -----------------------------------------------------------------------------------------------------

func (c *PeerConnection) RangeLocalFormat(f func(*Media, *Format)) {
	for _, m := range c.localMedias {
		for _, ft := range m.formats {
			f(m, ft)
		}
	}
}

func (c *PeerConnection) RangeRemoteFormat(f func(*Media, *Format)) {
	for _, m := range c.remoteMedias {
		for _, ft := range m.formats {
			f(m, ft)
		}
	}
}

// -----------------------------------------------------------------------------------------------------
func (c *PeerConnection) onSessionError(err error) {
	c.handle.OnSessionError(err)
}

// readUDP UDP读取数据, 无缓存
func (c *PeerConnection) readUDP() {

	var (
		conn   = c.udpConn
		buffer = make([]byte, 65536)
	)

	for {
		n, addr, err := conn.ReadFromUDP(buffer)
		if err != nil {
			log.Println(err)
			c.onSessionError(err)
			break
		}
		_ = addr

		if n == 0 {
			log.Println("read udp packet size 0")
			continue
		}

		c.recvPacket(buffer[:n])
	}

}

// readTCP TCP读取数据, 有ringbuffer缓存
func (c *PeerConnection) readTCP() {

	var (
		mutex        sync.Mutex
		cond         = sync.NewCond(&mutex)
		packetBuffer = packetio.NewBuffer()
	)

	go func() {
		buffer := make([]byte, 65536)
		for {
			n, err := packetBuffer.Read(buffer)
			if err != nil {
				log.Println(err)
				break
			}

			cond.Signal()

			c.recvPacket(buffer[:n])
		}
	}()

	const bufferSize = 2048
	var (
		buffer   = make([]byte, bufferSize)
		sizeBuff = make([]byte, 2)
		data     []byte
		err      error
		conn     = bufio.NewReader(c.tcpConn)
	)

	for {
		if sizeBuff[0], err = conn.ReadByte(); err != nil {
			log.Println(err)
			c.onSessionError(err)
			return
		}
		if sizeBuff[1], err = conn.ReadByte(); err != nil {
			log.Println(err)
			c.onSessionError(err)
			return
		}

		packetSize := binary.BigEndian.Uint16(sizeBuff)
		limitReader := io.LimitReader(conn, int64(packetSize))
		if uint16(bufferSize) > packetSize {
			var n int
			n, err = io.ReadFull(limitReader, buffer[:packetSize])
			data = buffer[:n]
		} else {
			data, err = io.ReadAll(limitReader)
		}

		if err != nil {
			log.Println(err)
			c.onSessionError(err)
			if len(data) == 0 {
				return
			} else {
				// EOF 放过
			}
		}

		if len(data) == 0 {
			log.Println("read tcp packet size 0")
			continue
		}

		for {
			if _, err = packetBuffer.Write(data); err != nil {
				if errors.Is(err, packetio.ErrFull) {
					// 缓冲区满了, 等待消费
					mutex.Lock()
					cond.Wait()
					mutex.Unlock()
				} else {
					log.Println(err)
					return
				}
			} else {
				break
			}
		}
	}
}

func (c *PeerConnection) recvPacket(data []byte) {
	var (
		h          = c.handle
		objectRtcp rtcp.Header
	)

	log.Printf("接收数据: %v", len(data))

	if err := objectRtcp.Unmarshal(data); err == nil {
		if objectRtcp.Type >= 192 && objectRtcp.Type <= 223 {
			// rtcp
			h.OnPacketRTCP(&objectRtcp, data)
			return
		}
	}

	var pkt rtp.Packet
	if err := pkt.Unmarshal(data); err != nil {
		log.Println(err)
		h.OnDecodeError(err)
		return
	}

	// 查找format
	{
		var cliFormat *Format
		for _, m := range c.remoteMedias {
			if m.media.BvSSRC != pkt.SSRC && m.media.SSRC != pkt.SSRC {
				continue
			}

			for _, f := range m.formats {
				if f.format.PayloadType() != pkt.PayloadType {
					continue
				}

				cliFormat = f
				break
			}
			break
		}

		if cliFormat != nil {
			cliFormat.readPacket(&pkt)
		} else {
			h.OnPacketLost(fmt.Errorf("not found format. %v", pkt.SSRC))
		}
	}
}

// 获取packet的pts
func (c *PeerConnection) packetPTS(f format.Format, pkt *rtp.Packet) (time.Duration, bool) {
	return c.timeDecoder.Decode(f, pkt)
}

// -----------------------------------------------------------------------------------------------------

// WritePacketRTCP 发送rtcp, 发送者报告/接收者报告
func (c *PeerConnection) WritePacketRTCP(m *description.Media, f format.Format, pkt rtcp.Packet) error {
	_, _ = m, f

	byts, err := pkt.Marshal()
	if err != nil {
		log.Println(err)
		return err
	}

	var header rtcp.Header
	_ = header.Unmarshal(byts)
	log.Printf("send RTCP: %v", header.Type.String())

	return c.write(byts)
}

// write tcp/udp发送数据
func (c *PeerConnection) write(data []byte) error {
	if conn := c.tcpConn; conn != nil {
		sizeBuff := make([]byte, 2)
		binary.BigEndian.PutUint16(sizeBuff, uint16(len(data)))
		n, err := conn.Write(append(sizeBuff, data...))
		_ = n
		if err != nil {
			c.onSessionError(err)
		}
		return err
	}

	if conn := c.udpConn; conn != nil {
		n, err := conn.Write(data)
		_ = n
		if err != nil {
			c.onSessionError(err)
		}
		return err
	}

	return nil
}

// -----------------------------------------------------------------------------------------------------
